﻿// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动！任何基于本项目二次开发而产生的一切法律纠纷和责任，我们不承担任何责任！

using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static StackExchange.Redis.Role;
using System.Web.Services.Description;
using Admin.NET.Application.Entity;
using S7.Net.Protocol;
using Aop.Api.Domain;
using NPOI.SS.Formula.Functions;
using System.Net.Sockets;
using AlibabaCloud.GatewaySpi;
using System.Net;
using S7.Net;
using S7.Net.Helper;
using NewLife;
using MQTTnet;
using MQTTnet.Formatter;
using Azure.Core;
using SL.NET.Core.Service;
using Microsoft.AspNetCore.Mvc;
using DataType = S7.Net.DataType;



namespace MyDemo.Application.Service.Plc;
public class PlcService : IDynamicApiController
{
    private readonly IMqttClient _plcmqttClient;
    private readonly MqttClientOptions _plcclientOptions;
    private bool _isConnected;
    public PlcService(SysCacheService sysCacheService, SqlSugarRepository<OrderInfoEntity> orderInfoEntity, UserManager userinfo)
    {
        // 构建 MQTT 客户端选项
        var optionsBuilder = new MqttClientOptionsBuilder()
           .WithTcpServer("localhost", 1888)
           .WithProtocolVersion(MqttProtocolVersion.V311)
           //.WithCredentials("admin", "admin")
           .WithClientId("WarehousId")
           .WithCleanSession()
           .WithTlsOptions(new MqttClientTlsOptions
           {
               UseTls = false
           });
          //_userinfo = userinfo;
        // _orderInfoEntity = orderInfoEntity;
        _plcclientOptions = optionsBuilder.Build();
        // 创建 MQTT 客户端
        _plcmqttClient = new MqttClientFactory().CreateMqttClient();

        //订阅消息接收事件
        //_mqttClient.ApplicationMessageReceivedAsync += HandleReceivedMessage;
        _isConnected = false;
        
        // Task.Run(async () => await _mqttClient.ConnectAsync(_clientOptions));

    }
   
  
    public class ConveyorSystemCommandDB20
    {
        // 可读标识(1=可读,2=不可读)
        public short ReadFlag { get; set; }

        // 应答标识(1=需要应答,2=不需要应答)
        public short AckFlag { get; set; }

        
            /// <summary>
            /// 指令下发
            /// </summary>
        public short Command { get; set; }

        /// <summary>
        /// 任务类型1：表示点位移动，（command==1时） 2：表示托盘码写入，（使用场景：扫码 枪了，人工补码，与任务不挂钩）
        /// </summary>
        public short TaskType { get; set; }

        // 3位设备编号(纯数字)
        public int DeviceNo { get; set; }

        // 任务号(10000-99999循环使用)
        public int TaskNo { get; set; }

        // 容器号(8位ASCII码，格式：2位字母+5位数字)
        public byte[] ContainerNo { get; set; }

        // 起始位置(3位编码)
        public int StartPosition { get; set; }

        // 目的位置(3位编码)
        public int TargetPosition { get; set; }

        // 预留字段
        public int Rel0 { get; set; }

        // 指令常量定义
        public static class Commands
        {
            // 普通输送线指令
            public const short Move = 1;               // 移动
            public const short Remove = 2;             // 移除
            public const short OutboundRequest = 4;    // 出库占用请求

            // 提升机指令
            public const short HoistMove = 11;         // 提升机移动
            public const short HoistUnlock = 12;       // 提升机解锁
        }

        // 任务类型常量定义
        public static class TaskTypes
        {
            public const short PointMove = 1;          // 点位移动
            public const short PalletCodeWrite = 2;    // 托盘码写入
        }

        // 数据验证方法
        public bool IsValid()
        {
            // 验证ReadFlag
            if (ReadFlag != 1 && ReadFlag != 2)
                return false;

            // 验证AckFlag
            if (AckFlag != 1 && AckFlag != 2)
                return false;

            // 验证Command
            if (!IsValidCommand(Command))
                return false;

            // 验证TaskType与Command的关联
            if (Command == Commands.Move && TaskType != TaskTypes.PointMove)
                return false;

            // 验证DeviceNo格式(3位数字)
            if (DeviceNo < 100 || DeviceNo > 999)
                return false;

            // 验证TaskNo范围
            if (TaskNo < 10000 || TaskNo > 99999)
                return false;

            // 验证ContainerNo格式
            string containerString = string.Empty;
            if (!IsValidContainerNo(ContainerNo,out containerString))
                return false;

            // 验证位置编码格式
            if (StartPosition < 100 || StartPosition > 999 ||
                TargetPosition < 100 || TargetPosition > 999)
                return false;

            return true;
        }

        private bool IsValidCommand(short command)
        {
            // 检查是否为有效指令
            return command switch
            {
                Commands.Move => true,
                Commands.Remove => true,
                Commands.OutboundRequest => true,
                Commands.HoistMove => true,
                Commands.HoistUnlock => true,
                _ => command >= 1 && command <= 99,
            };
        }

        /// <summary>
        /// 容器号校验
        /// </summary>
        /// <param name="containerNo">容器号字节数组</param>
        /// <returns>是否有效</returns>
        private bool IsValidContainerNo(byte[] containerNo,out string containerString)
        {
            if (containerNo == null || containerNo.Length != 8)
            {
                containerString = string.Empty;
                return false;
            }
            // 前两位为字母
            containerString = Encoding.ASCII.GetString(containerNo);
            if (!char.IsLetter(containerString[0]) || !char.IsLetter(containerString[1]))
                return false;

            // 后五位为数字
            for (int i = 2; i < 7; i++)
            {
                if (!char.IsDigit(containerString[i]))
                    return false;
            }

            // 最后一位为数字或字母
            if (!char.IsLetterOrDigit(containerString[7]))
                return false;
            return true;
        }


    }
  

    // With the following corrected code:  
    S7.Net.Plc __plc = new S7.Net.Plc(CpuType.S71200, "192.168.0.1", 0, 1);
    // 计算 DB 地址
    //byte[] containerBytes = __plc.DBRead(_baseDbNumber, CalculateDbAddress(Offsets.ContainerNo), VarType.Byte, 8);
   // ContainerNo = System.Text.Encoding.ASCII.GetString(containerBytes);

    public static async Task GetModbusTcpAsync()
    {
        // 创建 PLC 客户端
        //  TcpClient tcp = new TcpClient();
        TcpClient client = new TcpClient();
        //  IModbusMaster master = ModbusIpMaster.CreateIp(client);

        client.Connect(IPAddress.Parse("192.168.1.100"), 502); // 替换为实际的 IP 地址和端口号
                                                               //IModbusMaster master = ModbusIpMaster.CreateIp(client);

        //DB2o
        // 定义要写入的数据
       var  ConveyorSystemCommandDB20 = new ConveyorSystemCommandDB20()
       {
           AckFlag = 1,
           Command = 1,
           DeviceNo = 1,
           ReadFlag = 1,
            Rel0   =0,
           StartPosition = 100,
           TargetPosition = 200,
           TaskNo = 12345,
           TaskType = 1,  // 1表示点位移动
       };
        string containerNoString = "AB12345";
        ConveyorSystemCommandDB20.ContainerNo = Encoding.ASCII.GetBytes(containerNoString);



        var plc = new S7.Net.Plc(CpuType.S71200, "192.168.0.1", 0, 1);
        plc.Open();

        // MQTT 客户端


        var optionsBuilder = new MqttClientOptionsBuilder()
          .WithTcpServer("localhost", 1888)
          .WithProtocolVersion(MqttProtocolVersion.V311)
          //.WithCredentials("admin", "admin")
          .WithClientId("WarehousId")
          .WithCleanSession()
          .WithTlsOptions(new MqttClientTlsOptions
          {
              UseTls = false
          });

      //  var c = await ConnectToMqttAsync();
        // 读取 PLC 数据并发布到 MQTT
        while (true)
        {
            // Fix for CS7036: Add the missing required parameter "varCount" to the ReadAsync method call.  
            // The "varCount" parameter specifies the number of variables to read.  
            // Assuming the intention is to read 10 variables, the corrected code is as follows:  

            var data = await plc.ReadAsync(DataType.DataBlock, 1, 0, VarType.Word, 10, 0, CancellationToken.None);

            // 转换为 JSON 或其他格式
            var payload = System.Text.Json.JsonSerializer.Serialize(data);

            // 发布到 MQTT
            var message = new MqttApplicationMessageBuilder()
                .WithTopic("devices/hoist/telemetry\"")
                .WithPayload(payload)
                .Build();

          //  await _plcmqttClient.PublishAsync(message, CancellationToken.None);

            Thread.Sleep(10000); // 每秒发布一次
        }








        //// 创建 PLC 连接对象
        //// 参数: PLC 类型, IP 地址, 机架号, 插槽号
        //S7.Net.Plc plc = new Plc(CpuType.S71200, "192.168.0.1", 0, 1);

        //try
        //{
        //    // 连接到 PLC
        //    Console.WriteLine("正在连接到 PLC...");
        //    plc.Open();

        //        Console.WriteLine("连接成功！");
        //    if (plc.IsConnected)
        //    {

        //        // 读取 DB1.DBW0 的值 (一个字，16位)
        //        short dbValue = plc.DBRead(1, 0, VarType.Word, 1)[0];
        //        Console.WriteLine($"DB1.DBW0 的值: {dbValue}");

        //        // 读取 M0.0 位
        //        bool m0_0 = plc.DBRead(0, 0, VarType.Bit, 1, 0)[0];
        //        Console.WriteLine($"M0.0 的值: {m0_0}");

        //        // 写入 DB1.DBW2 的值
        //        short newValue = 12345;
        //        plc.DBWrite(1, 2, VarType.Word, new object[] { newValue });
        //        Console.WriteLine($"已将 {newValue} 写入 DB1.DBW2");

        //        // 写入 M0.1 位
        //        plc.DBWrite(0, 0, VarType.Bit, new object[] { true }, 1);
        //        Console.WriteLine("已将 M0.1 设置为 TRUE");
        //    }
        //    else
        //    {
        //        Console.WriteLine("连接失败！");
        //    }
        //}
        //catch (Exception ex)
        //{
        //    Console.WriteLine($"通信错误: {ex.Message}");
        //}
        //finally
        //{
        //    // 关闭连接
        //    if (plc.IsConnected)
        //    {
        //        plc.Close();
        //        Console.WriteLine("已断开与 PLC 的连接");
        //    }
        //}

        //Console.WriteLine("按任意键退出...");
        //Console.ReadKey();
    }














    /// <summary>
    /// 获取PLC正常行驶
    /// </summary>
    public void GetPlcormaldriving()
    {
        //连接到 PLC
        //using (var plc = new S7.Net.Plc(CpuType.S71200, "192.168.0.1", 0, 1))
        //{
        //    try
        //    {
        //        // 打开连接
        //        plc.Open();

        //        if (plc.IsConnected)
        //        {
        //            // 准备指令数据
        //            ushort controlCommand = 1; // 控制指令字
        //            ushort startQrCode = 10; // 起始二维码码值
        //            ushort middleQrCode = 0; // 中间二维码码值
        //            ushort targetQrCode = 11; // 目标二维码码值
        //            int totalDistance = 1185; // 起点到目标点的距离长度
        //            int middleToTargetDistance = 0; // 中间点到目标点的距离长度
        //            ushort direction = 2; // 小车运行方向
        //            ushort speed = 2200; // 小车运行速度
        //            ushort endBit = 1; // 指令结束位

        //            // 写入数据到 PLC
        //           // plc.DBWrite(0, 0, controlCommand); // Wm000
                    

        //            Console.WriteLine("指令下发成功！");
        //            // 等待指令完成
        //           // WaitForCommandCompletion(plc, targetQrCode);
        //        }
        //        else
        //        {
        //            Console.WriteLine("无法连接到 PLC。");
        //        }
        //    }
        //    catch (Exception ex)
        //    {
        //        Console.WriteLine($"发生错误: {ex.Message}");
        //    }
        //}
        //static void WaitForCommandCompletion(Plc plc, ushort targetQrCode)
        //{
        //    while (true)
        //    {
        //        // 读取 RGV 状态
        //        // 这里假设 RGV 状态存储在某个地址，需要根据实际情况修改
        //        bool isBusy = (bool)plc.DBRead(DataType.DataBlock, 0, 100, VarType.Bit, 1)[0]; // 示例地址
        //        ushort currentQrCode = (ushort)plc.DBRead(DataType.DataBlock, 0, 102, VarType.Word, 1)[0]; // 示例地址

        //        if (!isBusy && currentQrCode == targetQrCode)
        //        {
        //            Console.WriteLine("指令执行完成！");
        //            break;
        //        }

        //        // 等待一段时间后再次检查
        //        System.Threading.Thread.Sleep(1000);
        //    }
        //}
    }

    public   async Task ConnectToMqttAsync()
    {
        var connectResult = await _plcmqttClient.ConnectAsync(_plcclientOptions, CancellationToken.None);
        if (connectResult.ResultCode == MqttClientConnectResultCode.Success)
        {
            _isConnected = true;
            Console.WriteLine("Connected to MQTT server successfully.");
        }
        else
        {
            Console.WriteLine($"Failed to connect to MQTT server. Result code: {connectResult.ResultCode}");
        }
    }
}


	












